分类
联系方式
  1. 新浪微博
  2. E-mail

DartVM RawReceivePort

介绍

在 ReceivePort Dart 层实现中,可以看到其内部是基于 RawReceivePort 实现的。RawReceivePort 根据名字可以看出,是涉及 C/C++ 部分的。

RawReceivePort 抽象类

位于接口类位于 sdk/ilb/isolate/isolate.dart,是一个抽象类。

注解翻译

底层的异步消息接收器。

RawReceivePort 是一个底层特性,Zone 不知道 RawReceivePort 的存在。

该类接收一个 handler 参数,handler 总是能够在 Zone.root 下被触发,言外之意,该行为不受 Zone 控制。

该 Port 机制无法暂停。在发出第一个消息之前,handler 必须先注册,handler 注册前发出的消息都会丢失。

类声明

abstract class RawReceivePort {
  /// 创建一个长期活跃的接口来接收消息
  /// handler 可以在构造时传入,也可以后面通过 setter 传
  /// debugName 供调试工具使用
  external factory RawReceivePort([Function? handler, String debugName = '']);

  /// handler 的 setter
  /// 在 Dart 侧传入,默认 Zone.root 上回调,但是可以转 Zone:
  /// ```dart
  /// rawPort.handler = Zone.current.bind(actualHandler);
  /// ```
  void set handler(Function? newHandler);

  /// 关闭端口
  /// 关闭之后,后续收到的消息都丢弃了
  void close();

  /// 拿到这个 sendPort,就能够向 handler 发送消息
  SendPort get sendPort;
}

总结:现在还是疑惑具体的使用场景。更新:解答,在 Dart 侧的 ReceivePort,其内部实现类 _ReceivePortImpl 中,_ReceivePortImpl 的内部创建了 RawReceivePort。也就是说,实际工作都是在 RawReceivePort 中进行的。

构造函数的实现

构造函数的声明和实现是分离的,实现在 sdk/lib/_internal/vm/lib/isolate_patch.dart 中:

@patch
class RawReceivePort {
  @patch
  factory RawReceivePort([Function? handler, String debugName = '']) {
    _RawReceivePortImpl result = new _RawReceivePortImpl(debugName);
    result.handler = handler;
    return result;
  }
}

可以看到,具体的实现在 _RawReceivePortImpl。

_RawReceiverPortImpl

该类位于 sdk/lib/_internal/vm/lib/isolate_patch.dart。

静态全局 _portMap

这是一个全局静态的数据结构,所有 Port 机制的端口,都在这个结构中进行统一维护:

static final _portMap = <int, Map<String, dynamic>>{};

其中:

  • key:int 是 Port id
  • value:又是一个 Map,通常都只传了一对值 {'port': port},这里的 port 是 _RawReceiverPortImpl 实例。

创建

_RawReceiverPortImpl 构造相关的方法如下:

@pragma("vm:entry-point")
class _RawReceivePortImpl implements RawReceivePort {
  factory _RawReceivePortImpl(String debugName) {
    final port = _RawReceivePortImpl._(debugName);
    _portMap[port._get_id()] = <String, dynamic>{
      'port': port,
    };
    return port;
  }

  factory _RawReceivePortImpl._(String debugName)
      native "RawReceivePortImpl_factory";

  /**** Internal implementation details ****/
  int _get_id() native "RawReceivePortImpl_get_id";

其中:

  • 通过 _RawReceivePortImpl._ 创建 _RawReceivePortImpl 实例(C/C++ 层实际创建)
  • 创建出来的 _RawReceivePortImpl 实例放入 _portMap,port 的 id 获取也是在 C 层实现的


接着看 RawReceivePortImpl_factory,来到 runtime/lib/isolate.cc:

DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 2) {
  ASSERT(
      TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
  GET_NON_NULL_NATIVE_ARGUMENT(String, debug_name, arguments->NativeArgAt(1));
  Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
  return ReceivePort::New(port_id, debug_name, false /* not control port */);
}

其中:

  • C层还有一个 PortMap(推测在 C 和 Dart 层两者是对应关系)
  • 还要注意传入 PortMap::CreateMap 的 MessageHandler,这里取得是 isolate 的 message_handler
    • 这样底层 Port id 未来的唤起,唤醒的是 isolate,而不是 ReceivePort 上层传得 handler(stream.add)
    • 那 isolate 的 message_handler 里,必然有一个消息分发的过程:查询 Dart 侧的 PortMap,找到 Dart 的 receivePort,然后调用其 handler。
  • 最后一个 return,把 C 层创建的 ReceivePort 实例又抛会到了 Dart 层,与 _RawReceivePortImpl 关联

关于 C 层的 ReceivePort,参见 DartVM ReceivePort

Handler setter

该类中还有一个 handler setter 方法:

void set handler(Function? value) {
  final int id = this._get_id();
  if (!_portMap.containsKey(id)) {
    _portMap[id] = <String, dynamic>{
      'port': this,
    };
  }
  _portMap[id]!['handler'] = value;
}

可以看到,将传入的方法引用,如何添加到 _portMap 结构上。 这段代码实际使用场景是什么样的?在 Dart 侧的 _ReceivePortImpl,在其构造方法中有调用这个 setter:

_ReceivePortImpl.fromRawReceivePort(this._rawPort)
    : _controller = new StreamController(sync: true) {
  _controller.onCancel = close;
  _rawPort.handler = _controller.add;
}

双层 Handler 通信

从这里可以意识到,Dart 层跟 Native 层各有一套 ReceivePort 和 Handler 处理机制。一个套路在 Dart 层和 C/C++ 层使用了两遍。

我凭感觉梳理了一个示意图,可能还不太对:

因为向 Native 的 ReceivePort 传入的 handler 是 Isolate 的 message_handler。